Titre

---------------------------------------- Chapitre VII - Hachage ----------------------------------------

Deux exemples de protection basée sur des hash MD5 - 1/2

 

Cette fois-ci, nous allons voir deux exemples de protections basées sur des hash MD5.

Outils nécessaires :

OllyDbg (by Oleh Yuschuk)
Keygener Assitant v1.7 (by Mr Paradox & RobenHoodArab, http://www.at4re.com) pour les calculs, ou tout autre outil calculant des hashs MD5. J'aime bien Keygenner Assistant pour son aspect "Tout en un" et également car il intègre un Sniffer de hash et de crypto.
PEiD et son plugin kanal (by Jibz, Qwerton & snaker, http://www.peid.info/) ou tout autre analyseur d'exécutables !

- Les notions de base des précédents cours sur l’assembleur et l’utilisation d’Ollydbg sont considérées acquises.

RAPPEL

Tout le contenu de ce tutoriel est fourni à titre purement éducatif, et ludique. N'utilisez pas ce logiciel en dehors des autorisations fixées par la licence d'utilisation de celui-ci. Si vous ne disposez pas de licence valide pour ce logiciel, vous devez le désinstaller à l'échéance de sa période d'évaluation. Nous ne sommes en aucun cas responsable de l'usage que vous faites de ce programme et de ces tutoriels.

I - Premier cas : RegistryFast

Cible : RegistryFast v5.0
Editeur : RegistryFast.com

Je pense que l'on pourrait y attribuer un niveau de difficulté de 1 ou 1.5 sur une échelle de 5 (le serial correct n'étant pas comparé directement avec ce qui a été entré), l'exécutable n'est pas packé, ni obfusqué. De plus, aucun anti-debugger ni quoi que ce soit pour contrer l'analyse n'a été intégré...

Notre premier réflexe est bien sûr de scanner notre exécutable principal avec notre analyseur préféré. D'une manière générale, ProtectionID est celui que j'utilise le plus souvent pour la fiabilité de ses signatures et ses mises à jour encore régulières, mais, pour ce cas, je vais utiliser PEiD et le plugin Kanal, qui permet la détection de signatures cryptographiques :

PEiD

Kanal

Nous apprenons que notre cible est codée en Delphi, et surtout qu'elle ne semble pas être packée. Par ailleurs, Kanal nous indique l'utilisation d'un certain nombre de fonctions de crypto, que nous laisserons de côté pour le moment...

Ouvrons donc notre cible avec Olly, et tentons de nous enregistrer. Utilisez deux caractères numériques pour les 2 premières valeurs de votre serial... ou bien vous aurez droit à un beau: " "Votre lettre" is not a valid integrer value! " et vous n'obtiendrez pas le "bon" message d'erreur :

Invalid serial

On va pouvoir commencer à travailler :). Mettez Olly en Pause et allons jeter un œil dans la pile des Calls, appelé "Call Stack" :

Calls stack

Le but est de trouver un point antérieur à l'arrivée de la MessageBox, le plus prêt possible de la routine.
On aurait également pu faire des exécutions jusqu’au RET, nous serions arrivés au même point, mais il faut savoir s'économiser quelques clics ;). Vous allez double-cliquer sur la ligne à l'adresse 0012FB6C, ce qui nous conduira en 004DCAA4. En toute logique, il faudrait également aller voir en 0048A630... mais il n'y a rien d'intéressant là-bas ;).
Donc une fois en 004DCAA4, faîtes un petit coup de scroll down, voir si on peut se faire une idée de ce qui se trame dans cette routine...et Ô bonheur :

004DCC81 |. B9 50CD4D00  MOV ECX,RegFast.004DCD50     ; ASCII "Registry Fast"
004DCC86 |. BA 60CD4D00  MOV EDX,RegFast.004DCD60     ; ASCII "Invalid serial number! Please try again..."
[...]
004DCCA9 |. B9 50CD4D00  MOV ECX,RegFast.004DCD50     ; ASCII "Registry Fast"
004DCCAE |. BA 60CD4D00  MOV EDX,RegFast.004DCD60     ; ASCII "Invalid serial number! Please try again..."

Nul doute, nous devons être au bon endroit ;).

Posons donc un BP en 004DCAA4, et on recommence. Je saisis mon fake serial, pour moi, ce sera : " 59ABCDEFGHIJKL1111222223344445555" (notez que même si vous saisissez en minuscules, les caractères seront automatiquement mis en majuscules). N’oubliez pas de commencer par deux chiffres, puis on teste notre serial.

Comme prévu, nous breakons en 004DCAA4. Traçons un peu en step over, remarquez le CharLower :

004DCADF |. 8B45 EC MOV EAX,[LOCAL.5]          ; serial dans EAX
004DCAE2 |. 8D55 FC LEA EDX,[LOCAL.1]          ; pointeur vers le Buffer de reception
004DCAE5 |. E8 02C5F2FF CALL RegFast.00408FEC  ; Char Lower

On se retrouve avec "59abcdefghijkl1111222223344445555".

La partie suivante du code retire les éventuels tirets du serial :

004DCAF6 |. BA 20CD4D00 MOV EDX,RegFast.004DCD20   ; => Suivez cette valeur dans le dump pour voir qu’elle vaut 0x2Dh
004DCAFB |. 8B45 FC MOV EAX,[LOCAL.1]
004DCAFE |. E8 8523F3FF CALL RegFast.0040EE88

Ensuite, on attaque les choses plus sérieuses. Les commentaires vous épargneront un long discours…

Routine

Regardons tout ça. D’une manière générale, les opérations se font de la manière suivante :

MOV EAX, [Adresse du Buffer de réception]
PUSH EAX Param 1 (nombre de caractères) Param 2 (Position dans la chaîne)
MOV EAX, [Chaîne à modifier]
CALL FONCTION

Pour visualiser le résultat d’un CALL, il vous suffit de suivre EAX dans le dump, sélectionner le DWORD qui s’y trouve, et faire un " Follow DWORD in dump".

Dans l’ordre, nous aurons les actions suivantes :

Petite astuce : Vous pouvez définir les labels également sur les variables. Si vous souhaitez nommer les variables Chaine1, Chaine2, Chaine3, …, rendez vous à l’adresse en question dans le dump et appuyez sur la touche " : " pour personnaliser le label des variables :)

A ce niveau nous savons donc que notre serial devra faire au moins 8+ 9= 17d caractères sans les tirets. Et que les 2 premiers caractères doivent être compris entre 0 et 9. Nous savons que nous avons à faire à une constante, car elle est visible dans les SDR avant même de lancer le soft. Elle n’est donc pas créée dynamiquement. Et c’est tout !

Continuons notre route…

004DCC06 |. 8D45 C4       LEA EAX,[LOCAL.15]
004DCC09 |. 50            PUSH EAX
004DCC0A |. FF75 F8       PUSH [LOCAL.2]          ; 8 caractères de notre serial qu'on a appelé Chaine1
004DCC0D |. FF75 F0       PUSH [LOCAL.4]          ; les 10 Caractères de la constante que l'on a appelé Chaine4
004DCC10 |. FF75 F4       PUSH [LOCAL.3]          ; 3eme char...qu'on a appelé Chaine2 ;)
004DCC13 |. 8D45 BC       LEA EAX,[LOCAL.17]      ; Placement de l'adresse du Buffer dans EAX
004DCC16 |. BA 03000000   MOV EDX,3               ; Nbre de chaines à concaténer
004DCC1B |. E8 8882F2FF   CALL RegFast.00404EA8   ; Concatenation

On poursuit les opérations avec une concaténation : Chaine7 = Chaine1Chaine4Chaine2

004DCC20 |. 8B45 BC       MOV EAX,[LOCAL.17]      ; Chaine7 dans EAX
004DCC23 |. 8D55 C0       LEA EDX,[LOCAL.16]      ; Nouveau Buffer dans EDX
004DCC26 |. E8 F995FDFF   CALL RegFast.004B6224   ; HASH MD5 de Chaine7

Chaine7 est donc aussitôt placée dans EAX. Puis un buffer vide est placé dans EDX. En regardant la valeur de sortie de fonction, on remarque tout de suite un hash type MD5 :

Stack SS:[0012F7A4]=016615E0, (ASCII "63fc3ba6906c959c546ca204277df1ec")
EAX=0012F774

D’une part, c’est exactement la même longueur qu’un hash MD5 (32d caractères), de plus, Kanal nous avait sniffé, entre autres, du MD5. Et surtout, j’ai hashé Chaine7 en MD5 avec Keygenner Assistant, et j’ai eu le même résultat ;). La question est close !

Je sais pas pourquoi, mais je sens qu’on touche au but !

004DCC2B |. 8B45 C0        MOV EAX,[LOCAL.16]              ; Notre Hash dans EAX
004DCC2E |. B9 05000000    MOV ECX,5                       ; Nombre de caractères a récupérer
004DCC33 |. 8BD6            MOV EDX,ESI                     ; décalage dans le Hash de ESI qui vaut toujours Compteur1
004DCC35 |. E8 0E84F2FF    CALL RegFast.00405048
004DCC3A |. 8B55 C4        MOV EDX,[LOCAL.15]              ; On place dans EDX les 5 caractères récupérés
004DCC3D |. 8B45 FC        MOV EAX,[LOCAL.1]               ; On place notre Chaine6 dans EAX
004DCC40 |. E8 EF82F2FF    CALL RegFast.00404F34          ; Celui là, il ne serait pas bête d'y jeter un oeil ;)
004DCC45 |. 75 38          JNZ SHORT RegFast.004DCC7F     ; saute vers BadBoy si =! 0 :(
004DCC47 |. C683 30030000  MOV BYTE PTR DS:[EBX+330],1     ; Si on voulait cracker, on se pencherait sur cette variable ;)
004DCC4E |. 8D55 B4        LEA EDX,[LOCAL.19]
004DCC51 |. 8B83 F8020000  MOV EAX,DWORD PTR DS:[EBX+2F8]
004DCC57 |. E8 B0CDF8FF    CALL RegFast.00469A0C
004DCC5C |. 8B45 B4        MOV EAX,[LOCAL.19]
004DCC5F |. 8D55 B8        LEA EDX,[LOCAL.18]
004DCC62 |. E8 99C5F2FF    CALL RegFast.00409200
004DCC67 |. 8B45 B8        MOV EAX,[LOCAL.18]
004DCC6A |. E8 198E0400    CALL RegFast.00525A88
004DCC6F |. 8BC3           MOV EAX,EBX
004DCC71 |. E8 16010000    CALL RegFast.004DCD8C
004DCC76 |. 8BC3           MOV EAX,EBX
004DCC78 |. E8 53A0FAFF    CALL RegFast.00486CD0
004DCC7D |. EB 4E          JMP SHORT RegFast.004DCCCD     ; Saute au dessus des Badboy ;) , Surement vers le GoodBoy :)
004DCC7F |> 6A 30          PUSH 30
004DCC81 |. B9 50CD4D00    MOV ECX,RegFast.004DCD50       ; ASCII "Registry Fast"
004DCC86 |. BA 60CD4D00    MOV EDX,RegFast.004DCD60       ; ASCII "Invalid serial number! Please try again..."

On voit donc qu’on récupère 5 caractères dans notre hash à partir du ESIème caractère (qui vaut maintenant la valeur du 2ème caractère +1, notre Compteur1. En rentrant dans le CALL 00404F34, on voit bien que l’on compare la chaine extraite de notre hash avec Chaine6, soit les caractères 4 à 9 de notre serial entré. Récapitulons les dernières étapes :

On teste donc notre nouveau serial : "59a06c95ghijkl1111222223344445555" :

Congratulations

>Et voili, et voilou ! :D

Nous en avons terminé avec celui-ci, vous avez tout ce qu’il vous faut pour faire un petit keygen.

 

 

Precedent        Sommaire        Suivant